#include <stdio.h>
#include <string.h>
#include "DES.h"
#include "crack.h"

/*the locations where k2 is differing form k1
 *this numbers belongs to the locations in original key*/
int k2diffloc[8]={1,3,10,17,27,40,41,43};

/*this are the indices of the bits which are not present in k1
 *when k1 is mapped to k and these values are mappped form k2diffloc*/ 
int k1blankloc[8]={18,25,22,9,38,35,54,43};

/*this function splits the key and right shifts each half numbits time
 *this function reverses the effect of left shifting done to get the round
 *key form original key*/ 
void revKeyShift( uchar *key, const int numbits )
/* ----------------------------------------------------------------------- **
 * Split the 56-bit key in half & RIGHT rotate each half by <numbits> bits.
 *
 *  Input:  key     - The 56-bit key to be split-rotated.
 *          numbits - The number of bits by which to rotate the key.
 *
 *  Output: none.
 *
 *  Notes:  There are probably several better ways to implement this.
 *
 * ------------------------------------------------------------------------ **
 */
{
  int   i;
  uchar keep = key[6];  /* Copy the lower order bits of the key. */
  /* Repeat the shift process <numbits> times.
   */
  for( i = 0; i < numbits; i++ ){
    int j;
    /* Shift the entire thing, byte by byte.
     */ 
    for( j = 6; j >=0; j-- ){
      if( j!=6  && (key[j] & 0x01) )  /* If the top bit of this byte is set. */
	key[j+1] |=  0x80;        /* ...shift it to last byte's low bit. */
      key[j] >>= 1;               /* Then right-shift the whole byte.     */
    }
    /* Now move the high-order bits of each 28-bit half-key to their
     * correct locations.
     * Bit 28 is the heighest order bit of the second half-key.
     * Before the shift, it was the lowest order bit of the 1st half-key.
     */
    if( GETBIT( key, 28 ) ){     /* If bit 28 is set... */
      CLRBIT( key, 28 );        /* ...clear bit 28. */
      SETBIT( key, 0 );        /* ...set heighest order bit of 1st half-key. */
    }
    /* We kept the highest order bit of the first half-key in <keep>.
     * If it's set, copy it to bit 27.
     */
    if( keep & 0x01 )
      SETBIT( key, 28 );
    /* Rotate the <keep> byte too, in case <numbits> is 2 and there's
     * a second round coming.
     */
    keep >>= 1;
  }
} /* KeyShift */

/*converts 56 bit key to 64 bit key by reading chunks of 7 bits from 
 *56 bit key and copying it to dst and placing lsb as 0*/ 
void convert7to8(uchar *dst,uchar *src)
{  
  int dst_i=0,j,i; 
  int src_i=0;
  for(j=0; j<8; j++ ){
    for( i = 0 ; i < 7; i++ ){
      if(GETBIT(src, src_i)){
	SETBIT(dst,dst_i);  
      }
      else{
	CLRBIT(dst,dst_i);
      }
      src_i++; 
      dst_i++;
    }
    CLRBIT(dst,dst_i);
    dst_i++;
  }  
}

/*Depending on the original key and paintext we may get multiple k1 amd k2
 *so in order to find original key from the multiple round keys we need to
 *exploit the fact that valid (k1 k2) pair will have exactly 40 bits in 
 *common*/
int check_40bits_common(uchar *k1,uchar *k2)
{
  int i;
  for(i=0;i<48;i++){ 
    if((map_K1toK2[i])!=100){
      if(GETBIT(k1, i) == GETBIT(k2, map_K1toK2[i])){
        continue;
      }
      else{
	return 0;
      } 
    }
  } 
  return 1;
}

int main(int argc, char** argv) {
  int i,j,k;
  uchar c;
  FILE *fp1,*fp2,*fp3;
  if(argc != 3) {
    fputs("Usage: crack PLAIN_TEXT_FILE CIPHER_TEXT_FILE\n", stderr);
    return 1;
  }  

  /* Initialize input and output to null-filled buffer */
  uchar input[MAX_LENGTH]; 
  uchar output[MAX_LENGTH]; 

  for(i = 0; i < MAX_LENGTH; i++){
    input[i] = '\0';
    output[i] = '\0';
  }

  fp1=fopen(argv[1],"rb");
  fp3=fopen("L0R0.txt","wb");

  /* Read plain text from INPUT_FILE */
  fread((void*)input,sizeof(uchar),MAX_LENGTH,fp1);
  
  /* Determine total number of blocks read */
  BLOCKS = (strlen(input)/8)+1;

  /* Temp */
  uchar D1[8];

  /* Find LO RO for each block*/
  for(i = 0; i < BLOCKS; i++) {
    fprintf(fp3,"\n");
    Permute( D1, input+i*8, InitialPermuteMap, 8 );    
    for(j=0; j<4; j++){
      fprintf(fp3,"%02X",D1[j]);
      L0[i][j] = D1[j];
    }
    fprintf(fp3,"\t");
    for(j=0; j<4; j++){
      fprintf(fp3,"%02X",D1[j+4]);
      R0[i][j] = D1[j+4];
      L1[i][j] = D1[j+4];
    }
  }
  fclose(fp1);
  fclose(fp3);
  /* Reset input & output array */
  for(i = 0; i < MAX_LENGTH; i++){
    input[i] = '\0';
    output[i] = '\0';
  }

  /* Now read ciphertext file to get L2 & R2 */
  fp2=fopen(argv[2],"rb");
  fread((void*)input,sizeof(uchar),MAX_LENGTH,fp1);

  /* Find L2 R2 for each block*/
  fp3=fopen("L2R2.txt","wb");
  for(i = 0; i < BLOCKS; i++) {
    fprintf(fp3,"\n");	
    Permute( D1, input+i*8, InverseFinalPermuteMap, 8 );    
    for(j=0; j<4; j++){
      fprintf(fp3,"%02X",D1[j]);
      L2[i][j] = D1[j];
      R1[i][j] = D1[j];
    }
    fprintf(fp3,"\t");
    for(j=0; j<4; j++)
      {
	fprintf(fp3,"%02X",D1[j+4]);
	R2[i][j] = D1[j+4];
      }
  }
  fclose(fp2);
  fclose(fp3);
  int k1_cnt,k2_cnt;
  k1_cnt = crack_K1();  
  k2_cnt = crack_K2();
  for(i=0;i<k1_cnt;i++)
    printHex("K1",k1_match[i], 6);
  for(i=0;i<k2_cnt;i++)
    printHex("K2",k2_match[i], 6);
  
  
  /*finding key K from K1 and K2*/
  uchar key1[7],temp[7];
  int l;
  for(k=0;k<k1_cnt;k++){
    //inverse key compression (48 -> 56 bit) 
    for(i=0;i<48;i++){
      if(GETBIT(k1_match[k],i)){
	SETBIT(key1,InvKeyComp[i]);
      }
      else{
	CLRBIT(key1,InvKeyComp[i]);
      }
    }
    //reverse shifting i.e. right shift
    revKeyShift(key1, 1);
    for(l=0;l<k2_cnt;l++){
      if(check_40bits_common(k1_match[k],k2_match[l])){
	for(i=0;i<8;i++){
	  if(GETBIT(k2_match[l],k2diffloc[i])){
	    SETBIT(key1,k1blankloc[i]);
	  }
	  else{
	    CLRBIT(key1,k1blankloc[i]);
	  }
	}
	uchar final_key[8];
	Permute(temp,key1,InverseKeyPermuteMap,7);
	convert7to8(final_key,temp);
	for(i=0;i<8;i++){
	  final_key[i]>>=1;		
	}
	printf("\nK:");
	for(i=0;i<8;i++)
	  printf("%c",final_key[i]);
	printf("\n");	
      }
    }
  }
  return 0;
}
